# simulator/radar_moni.py
"""
雷达模拟器 - 负责产生雷达发射信号和处理回波信号，并能够检测目标位置
Radar Simulator - Generates radar transmit signals, processes echo signals, and detects targets
"""

import numpy as np
import pandas as pd
import os
from scipy import signal
from typing import Tuple, Dict, Any, List
from dataclasses import dataclass
from enum import Enum


class RadarMode(Enum):
    """雷达工作模式"""
    SEARCH = "search"  # 搜索模式 - 单脉冲测距，大波门
    TRACK = "track"    # 跟踪模式 - 多脉冲相参积累，RD图，小波门


@dataclass
class TransmitSignalData:
    """发射信号数据结构"""
    iq_samples: np.ndarray          # IQ信号采样点
    time_axis: np.ndarray           # 时间轴
    pulse_start_time: float         # 脉冲起始时间 (s)
    pulse_end_time: float           # 脉冲终止时间 (s)
    pulse_width: float              # 脉宽 (s)
    rf_frequency: float             # 射频频率 (Hz)
    sampling_rate: float            # 采样率 (Hz)
    range_gate_start: float         # 距离波门起始 (m)
    range_gate_width: float         # 距离波门宽度 (m)
    pulse_type: str                 # 脉冲类型 ('chirp', 'cw', 'rect')
    bandwidth: float                # 信号带宽 (Hz)

    def get_range_gate_end(self) -> float:
        """获取距离波门终止距离 (m)"""
        return self.range_gate_start + self.range_gate_width

class RadarMoni:
    """雷达模拟器 - 负责雷达发射信号生成、回波处理和目标检测"""

    # 类级别的标志，用于控制差斜率参数只打印一次
    _slope_params_printed = False

    def __init__(self, radar_params: Dict):
        self.params = radar_params
        self.fs = self.params.get('sampling_rate', 100.0) * 1e6  # 采样频率，从MHz转换为Hz
        self.c = 3.0e8    # 光速
        self.time_stamp = 0.0
        self.current_tx_signal = None  # 当前发射信号（用于信号处理）
        self.current_tx_data = None    # 当前发射信号数据结构

        # 工作模式状态
        self.current_mode = RadarMode.SEARCH  # 初始为搜索模式
        self._pending_mode_switch = None  # 待切换的模式（在下一个PRT生效）
        self.target_detected_range = None  # 检测到的目标距离 (m)（用于模式切换）
        self.search_detection_count = 0  # 搜索模式下连续检测次数
        self.search_detection_threshold = 3  # 连续检测3次后切换到跟踪模式
        self.track_loss_count = 0  # 跟踪模式下连续丢失次数
        self.track_loss_threshold = 10  # 连续丢失10次后退回搜索模式

        # 脉冲积累缓冲区（用于多普勒处理和单脉冲测角）
        self.coherent_integration_num = radar_params.get('coherent_integration_num', 64)  # 相参积累脉冲数
        self.pulse_compression_matrix = None  # 和通道矩阵
        self.pulse_compression_matrix_az_diff = None  # 方位差通道矩阵
        self.pulse_compression_matrix_el_diff = None  # 俯仰差通道矩阵
        self.pulse_count = 0  # 当前积累的脉冲数

        # 加载差斜率参数（用于单脉冲测角）
        self._load_difference_slope_parameters()

    def generate_transmit_signal(self, pulse_width: float = None,
                                pulse_type: str = 'chirp') -> TransmitSignalData:
        """生成发射信号，返回完整的信号数据结构"""
        # 处理待执行的模式切换
        if self._pending_mode_switch is not None:
            self.current_mode = self._pending_mode_switch
            self._pending_mode_switch = None
            print(f"[雷达模式切换生效] 当前模式: {self.current_mode.value}")

        # 根据工作模式动态设置采样率和距离波门
        if self.current_mode == RadarMode.SEARCH:
            # 搜索模式 - 低采样率、大波门
            self.fs = self.params.get('search_sampling_rate', 10.0) * 1e6  # 10 MHz
            range_gate_start = self.params.get('search_range_gate_start', 5.0) * 1000  # 配置是km，转换为m
            range_gate_width = self.params.get('search_range_gate_width', 100.0) * 1000  # 配置是km，转换为m
        else:  # TRACK mode
            # 跟踪模式 - 高采样率、小波门
            self.fs = self.params.get('track_sampling_rate', 100.0) * 1e6  # 100 MHz
            if self.target_detected_range is not None:
                # 跟踪窗口 = 跟踪容差 + 脉冲宽度对应距离 + 运动容差
                track_margin = self.params.get('track_range_gate_margin', 0.5) * 1000  # 配置是km，转换为m
                pulse_width_val = self.params.get('pulse_width', 10.0) * 1e-6  # 秒
                pulse_range = (self.c * pulse_width_val) / 2  # 脉冲宽度对应距离（m）≈1500 m

                # 考虑目标运动：相参积累期间目标可能移动的距离
                # 假设最大径向速度 ±35 m/s，相参积累时间 = 64 * PRT = 64 ms
                prt = self.params.get('pulse_repetition_time', 1.0) * 1e-3  # 秒
                coherent_time = self.params.get('coherent_integration_num', 64) * prt  # 相参积累时间
                max_velocity = 35.0  # m/s (假设最大径向速度)
                motion_margin = max_velocity * coherent_time  # m

                # 总的半波门宽度 = 基本容差 + 脉冲半宽度 + 运动容差
                total_margin = track_margin + pulse_range / 2 + motion_margin  # m

                range_gate_start = max(0, self.target_detected_range - total_margin)
                range_gate_width = 2 * total_margin  # m
            else:
                # 如果没有目标位置，退回搜索模式
                self.current_mode = RadarMode.SEARCH
                self.fs = self.params.get('search_sampling_rate', 10.0) * 1e6
                range_gate_start = self.params.get('search_range_gate_start', 5.0) * 1000  # 配置是km，转换为m
                range_gate_width = self.params.get('search_range_gate_width', 100.0) * 1000  # 配置是km，转换为m

        if pulse_width is None:
            pulse_width = self.params.get('pulse_width', 10.0) * 1e-6  # 转换为秒

        # 时间轴 - 使用正确的采样方式确保连续性
        num_samples = int(pulse_width * self.fs)
        # 使用 arange 确保采样间隔严格为 1/fs
        t = np.arange(num_samples) / self.fs
        timeVec = self.time_stamp + t

        # 获取雷达参数
        bandwidth = self.params.get('bandwidth', 10.0) * 1e6  # Hz
        rf_frequency = self.params.get('frequency', 10.0) * 1e9  # Hz
        tx_power = self.params.get('power', 1500.0)  # W

        # 根据发射功率和阻抗计算信号幅度
        # 功率关系: P = V²/R
        # 电压幅度: V = √(P × R)
        # 对于复数信号（I/Q），幅度: A = V/√2
        impedance = 50.0  # 欧姆
        voltage = np.sqrt(tx_power * impedance)  # V
        signal_amplitude = voltage / np.sqrt(2)  # 复数信号幅度

        if pulse_type == 'chirp':
            # 线性调频信号（LFM）- 改进的chirp实现
            # 使用标准的LFM chirp公式：s(t) = exp(j*π*k*t²)，其中k=B/T
            k = bandwidth / pulse_width  # 调频斜率 Hz/s

            # 时间轴从-T/2到+T/2，这是标准的chirp定义
            t_centered = t - pulse_width / 2

            # 标准LFM chirp: s(t) = A × exp(j*π*k*t²)
            # 瞬时频率: f(t) = k*t，从-B/2到+B/2
            phase = np.pi * k * t_centered**2
            signal_tx = signal_amplitude * np.exp(1j * phase)

            # 应用汉宁窗来改善旁瓣特性（可选）
            # window = np.hanning(len(t))
            # signal_tx = signal_tx * window
        elif pulse_type == 'cw':
            # 连续波信号
            signal_tx = signal_amplitude * np.ones(len(t), dtype=complex)
        else:
            # 矩形脉冲
            signal_tx = signal_amplitude * np.ones(len(t), dtype=complex)

        # 构建发射信号数据结构
        tx_data = TransmitSignalData(
            iq_samples=signal_tx,
            time_axis=timeVec,
            pulse_start_time=self.time_stamp,
            pulse_end_time=self.time_stamp + pulse_width,
            pulse_width=pulse_width,
            rf_frequency=rf_frequency,
            sampling_rate=self.fs,
            range_gate_start=range_gate_start,
            range_gate_width=range_gate_width,
            pulse_type=pulse_type,
            bandwidth=bandwidth
        )

        # 保存当前发射信号（用于后续的信号处理）
        self.current_tx_signal = signal_tx
        self.current_tx_data = tx_data

        # 更新时间戳（脉冲重复时间）
        prt = self.params.get('pulse_repetition_time', 1.0) * 1e-3  # ms转换为s
        self.time_stamp += prt

        return tx_data

    def _load_difference_slope_parameters(self):
        """加载差斜率参数CSV文件"""
        # 获取config目录路径
        config_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'config')
        slope_file = os.path.join(config_dir, 'difference_slope_parameters.csv')

        # 初始化为None
        self.az_diff_slope = None
        self.el_diff_slope = None
        self.az_diff_slope_normalized = None
        self.el_diff_slope_normalized = None

        if not os.path.exists(slope_file):
            print(f"[RadarMoni] 警告：未找到差斜率参数文件: {slope_file}")
            print("[RadarMoni] 单脉冲测角功能将不可用")
            return

        try:
            # 读取CSV文件
            df = pd.read_csv(slope_file, encoding='utf-8')

            # 提取参数
            self.az_diff_slope = float(df['方位差斜率_幅度每度'].iloc[0])
            self.el_diff_slope = float(df['俯仰差斜率_幅度每度'].iloc[0])
            self.az_diff_slope_normalized = float(df['归一化方位差斜率_每度'].iloc[0])
            self.el_diff_slope_normalized = float(df['归一化俯仰差斜率_每度'].iloc[0])

            # 只在第一次加载时打印
            if not RadarMoni._slope_params_printed:
                print(f"[RadarMoni] 差斜率参数已加载:")
                print(f"  方位差斜率: {self.az_diff_slope:.4f} (幅度/度)")
                print(f"  俯仰差斜率: {self.el_diff_slope:.4f} (幅度/度)")
                RadarMoni._slope_params_printed = True

        except Exception as e:
            print(f"[RadarMoni] 加载差斜率参数失败: {e}")
            self.az_diff_slope = None
            self.el_diff_slope = None

    def monopulse_angle_estimation(self, sum_amplitude: float, az_diff_amplitude: float,
                                   el_diff_amplitude: float) -> Tuple[float, float]:
        """
        单脉冲比幅测角：根据和通道与差通道的幅度比估计目标角度偏差

        单脉冲测角原理：
        - 标准公式：θ = (Δ/Σ) / k_norm
        - 其中 Δ/Σ 是差和比，k_norm 是归一化差斜率 (单位：1/度)
        - 归一化差斜率定义：k_norm = d(Δ/Σ)/dθ

        Args:
            sum_amplitude: 和通道回波幅度
            az_diff_amplitude: 方位差通道回波幅度
            el_diff_amplitude: 俯仰差通道回波幅度

        Returns:
            (azimuth_error, elevation_error): 方位角误差(度)，俯仰角误差(度)
        """
        if self.az_diff_slope_normalized is None or sum_amplitude == 0:
            return (0.0, 0.0)

        # 计算差和比
        az_ratio = az_diff_amplitude / sum_amplitude  # 方位差和比 Δ_az/Σ
        el_ratio = el_diff_amplitude / sum_amplitude  # 俯仰差和比 Δ_el/Σ

        # 单脉冲测角公式：θ = (Δ/Σ) / k_norm
        # 使用CSV文件中的归一化差斜率 (1/度)
        azimuth_error = az_ratio / self.az_diff_slope_normalized
        elevation_error = el_ratio / self.el_diff_slope_normalized

        return (azimuth_error, elevation_error)

    def signal_process(self, sum_echo: np.ndarray, az_diff_echo: np.ndarray = None,
                      el_diff_echo: np.ndarray = None) -> Dict:
        """
        雷达信号处理 - 每次处理一个PRT周期的三路回波信号

        处理流程:
        - 三路信号分别进行：接收机增益 → 噪声 → 脉冲压缩 → 相参积累
        - 搜索模式: 单脉冲脉压检测，快速搜索目标（仅使用和通道）
        - 跟踪模式: 相参积累 + RD图 + CFAR检测 + 单脉冲测角（使用三路）

        Args:
            sum_echo: 和通道回波信号 (np.ndarray)
            az_diff_echo: 方位差通道回波信号 (np.ndarray, optional)
            el_diff_echo: 俯仰差通道回波信号 (np.ndarray, optional)

        Returns:
            Dict: 处理结果，包含:
                - measurements: 测量结果列表
                - radar_mode: 当前工作模式
                - target_detected_range: 检测到的目标距离
                - (其他根据模式的不同字段)
        """

        # 检查是否有发射信号参考
        if self.current_tx_signal is None:
            raise ValueError("未找到发射信号参考。请先调用 generate_transmit_signal() 生成发射信号。")

        # 步骤1: 接收机增益放大（三路）
        amplified_sum = self._apply_receiver_gain(sum_echo)
        amplified_az_diff = self._apply_receiver_gain(az_diff_echo) if az_diff_echo is not None else None
        amplified_el_diff = self._apply_receiver_gain(el_diff_echo) if el_diff_echo is not None else None

        # 步骤2: 添加接收机热噪声（三路独立噪声）
        rx_sum_with_noise = self._add_receiver_noise(amplified_sum)
        rx_az_diff_with_noise = self._add_receiver_noise(amplified_az_diff) if amplified_az_diff is not None else None
        rx_el_diff_with_noise = self._add_receiver_noise(amplified_el_diff) if amplified_el_diff is not None else None

        # 步骤3: 脉冲压缩 (匹配滤波) - 三路
        compressed_sum = self.pulse_compression(rx_sum_with_noise, self.current_tx_signal)
        compressed_az_diff = self.pulse_compression(rx_az_diff_with_noise, self.current_tx_signal) if rx_az_diff_with_noise is not None else None
        compressed_el_diff = self.pulse_compression(rx_el_diff_with_noise, self.current_tx_signal) if rx_el_diff_with_noise is not None else None

        # 根据工作模式选择处理方式
        if self.current_mode == RadarMode.SEARCH:
            # ========== 搜索模式 - 单脉冲检测（仅使用和通道）==========
            result = self._process_search_mode(compressed_sum)

        else:  # TRACK mode
            # ========== 跟踪模式 - 相参积累 + RD图 + 单脉冲测角 ==========
            result = self._process_track_mode(compressed_sum, compressed_az_diff, compressed_el_diff)

        # 添加当前模式信息和信号数据
        result['radar_mode'] = self.current_mode.value
        result['target_detected_range'] = self.target_detected_range
        result['compressed_signal'] = compressed_sum  # 和通道脉冲压缩信号
        result['compressed_az_diff'] = compressed_az_diff  # 方位差通道脉冲压缩信号
        result['compressed_el_diff'] = compressed_el_diff  # 俯仰差通道脉冲压缩信号
        result['receiver_signal'] = rx_sum_with_noise  # 和通道接收机信号

        return result

    def _process_search_mode(self, compressed_signal: np.ndarray) -> Dict:
        """
        搜索模式处理 - 单脉冲脉压检测

        Args:
            compressed_signal: 脉冲压缩后的信号

        Returns:
            处理结果字典
        """
        range_bins = len(compressed_signal)

        # 计算距离轴（基于波门起始和采样率）
        # 距离轴 = 波门起始 + (采样点索引 / 采样率) × 光速 / 2
        range_gate_start = self.current_tx_data.range_gate_start  # m
        sampling_rate = self.current_tx_data.sampling_rate  # Hz
        range_gate_width = (range_bins / sampling_rate) * (self.c / 2)  # m
        range_gate_end = range_gate_start + range_gate_width
        range_axis = np.linspace(range_gate_start, range_gate_end, range_bins)  # m

        # 单脉冲检测 - 简单阈值检测
        compressed_magnitude = np.abs(compressed_signal)

        # 计算噪声电平（使用统计方法）
        noise_power = np.median(compressed_magnitude)  # 使用中值作为噪声估计
        threshold = noise_power * 10  # 10倍噪声作为阈值

        # 查找峰值
        peaks_idx = np.where(compressed_magnitude > threshold)[0]
        

        measurements = []
        if len(peaks_idx) > 0:
            # 找到最强的峰值
            strongest_peak_idx = peaks_idx[np.argmax(compressed_magnitude[peaks_idx])]

            # 通过采样点索引和采样率计算目标距离
            # peak_range = (采样点数 / 采样率) × 光速 / 2 + 波门起始距离
            peak_range_relative = strongest_peak_idx / self.current_tx_data.sampling_rate * self.c / 2  # 相对于波门起始的距离 (m)
            print(f"Peak Range:{peak_range_relative}")
            range_gate_start = self.current_tx_data.range_gate_start  # 波门起始 (m)
            peak_range = peak_range_relative + range_gate_start  # 绝对距离 (m)

            peak_amplitude = compressed_magnitude[strongest_peak_idx]

            # 计算SNR
            snr_db = 20 * np.log10(peak_amplitude / noise_power) if noise_power > 0 else 0

            measurement = {
                'range': peak_range,  # m
                'doppler': 0,  # 单脉冲无多普勒信息
                'velocity': 0,
                'amplitude': peak_amplitude,
                'snr': snr_db
            }
            measurements.append(measurement)

            # 更新检测状态
            if self.target_detected_range is None:
                self.target_detected_range = peak_range  # m
                self.search_detection_count = 1
            else:
                # 检查是否是同一个目标（距离相近）
                if abs(peak_range - self.target_detected_range) < 2000:  # 2000m容差
                    self.search_detection_count += 1
                    # 更新目标距离（取平均）
                    self.target_detected_range = (self.target_detected_range + peak_range) / 2
                else:
                    # 新目标
                    self.target_detected_range = peak_range  # m
                    self.search_detection_count = 1

            # 检查是否切换到跟踪模式（切换将在下一个PRT生效）
            if self.search_detection_count >= self.search_detection_threshold:
                # 注意：不在这里立即切换，而是标记需要切换
                # 模式切换会在下次generate_transmit_signal时生效，确保波门参数一致
                self._pending_mode_switch = RadarMode.TRACK
                print(f"[雷达模式切换请求] 搜索 -> 跟踪, 目标距离: {self.target_detected_range:.2f} m")

        else:
            # 未检测到目标
            self.search_detection_count = 0
            self.target_detected_range = None

        result = {
            'measurements': measurements,
            'range_axis': range_axis,  # m
            'accumulated_pulses': 1,
            'coherent_integration_complete': False,
            'processing_mode': 'search'
        }

        return result

    def _process_track_mode(self, compressed_sum: np.ndarray,
                           compressed_az_diff: np.ndarray = None,
                           compressed_el_diff: np.ndarray = None) -> Dict:
        """
        跟踪模式处理 - 三路相参积累 + RD图 + 单脉冲测角

        Args:
            compressed_sum: 和通道脉冲压缩后的信号
            compressed_az_diff: 方位差通道脉冲压缩后的信号
            compressed_el_diff: 俯仰差通道脉冲压缩后的信号

        Returns:
            处理结果字典
        """
        range_bins = len(compressed_sum)

        # 初始化三路矩阵（第一次调用时）
        # 矩阵形状: (num_pulses, range_bins) - 每行是一个脉冲的压缩结果
        if self.pulse_compression_matrix is None:
            self.pulse_compression_matrix = np.zeros((self.coherent_integration_num, range_bins), dtype=complex)
            self.pulse_compression_matrix_az_diff = np.zeros((self.coherent_integration_num, range_bins), dtype=complex)
            self.pulse_compression_matrix_el_diff = np.zeros((self.coherent_integration_num, range_bins), dtype=complex)
            self.pulse_count = 0

        # 存入三路矩阵的当前行
        self.pulse_compression_matrix[self.pulse_count, :] = compressed_sum
        if compressed_az_diff is not None:
            self.pulse_compression_matrix_az_diff[self.pulse_count, :] = compressed_az_diff
        if compressed_el_diff is not None:
            self.pulse_compression_matrix_el_diff[self.pulse_count, :] = compressed_el_diff
        self.pulse_count += 1

        # 检查是否完成相参积累
        if self.pulse_count >= self.coherent_integration_num:
            # 完成相参积累 - 生成RD图并检测

            # 计算距离轴（基于波门起始和采样率）
            range_gate_start = self.current_tx_data.range_gate_start  # m
            sampling_rate = self.current_tx_data.sampling_rate  # Hz
            range_gate_width = (range_bins / sampling_rate) * (self.c / 2)  # m
            range_gate_end = range_gate_start + range_gate_width
            range_axis = np.linspace(range_gate_start, range_gate_end, range_bins)  # m

            # 计算多普勒轴
            prt = self.params.get('pulse_repetition_time', 1.0) * 1e-3  # ms转换为s

            # 使用fftfreq生成频率轴，然后fftshift，确保与FFT输出完美对应
            # fftfreq的参数d是采样间隔（PRT），生成的频率范围是 [-PRF/2, PRF/2)
            doppler_axis = np.fft.fftshift(np.fft.fftfreq(self.coherent_integration_num, prt))

            # 沿慢时间维(第0维,列)进行FFT生成RD图 - 三路
            # 矩阵形状: (num_pulses, range_bins) = (32, 7664)
            # 对每一列(慢时间维)进行FFT: axis=0
            # FFT后: (doppler_bins, range_bins) = (32, 7664)
            rd_map_sum = np.fft.fftshift(np.fft.fft(self.pulse_compression_matrix, axis=0), axes=0)
            rd_map_az_diff = np.fft.fftshift(np.fft.fft(self.pulse_compression_matrix_az_diff, axis=0), axes=0) if compressed_az_diff is not None else None
            rd_map_el_diff = np.fft.fftshift(np.fft.fft(self.pulse_compression_matrix_el_diff, axis=0), axes=0) if compressed_el_diff is not None else None

            rd_map_magnitude = np.abs(rd_map_sum)

            # 简化检测：直接找最大值作为目标（使用和通道）
            max_idx = np.unravel_index(np.argmax(rd_map_magnitude), rd_map_magnitude.shape)
            doppler_idx, range_idx = max_idx

            # 提取三路目标参数
            target_range = range_axis[range_idx]  # m
            target_doppler = doppler_axis[doppler_idx]  # Hz
            target_amplitude_sum = rd_map_magnitude[doppler_idx, range_idx]

            # 提取差通道相应位置的幅度
            target_amplitude_az_diff = np.abs(rd_map_az_diff[doppler_idx, range_idx]) if rd_map_az_diff is not None else 0.0
            target_amplitude_el_diff = np.abs(rd_map_el_diff[doppler_idx, range_idx]) if rd_map_el_diff is not None else 0.0

            # 单脉冲测角
            azimuth_error, elevation_error = self.monopulse_angle_estimation(
                target_amplitude_sum, target_amplitude_az_diff, target_amplitude_el_diff
            )

            # 计算速度
            wavelength = self.c / (self.params.get('frequency', 10.0) * 1e9)
            target_velocity = -target_doppler * wavelength / 2  # m/s

            # 计算SNR（简化：相对于平均噪声）
            noise_floor = np.mean(rd_map_magnitude)
            snr_db = 20 * np.log10(target_amplitude_sum / noise_floor) if noise_floor > 0 else 0

            # 构建测量结果（包含单脉冲测角结果）
            measurements = [{
                'range': target_range,  # m
                'doppler': target_doppler,  # Hz
                'velocity': target_velocity,  # m/s
                'amplitude': target_amplitude_sum,
                'snr': snr_db,
                'azimuth_error': azimuth_error,  # 度
                'elevation_error': elevation_error  # 度
            }]

            # 打印检测结果
            print(f"[检测] 距离={target_range:.1f}m, 速度={target_velocity:.1f}m/s, SNR={snr_db:.1f}dB, 方位={azimuth_error:.2f}°, 俯仰={elevation_error:.2f}°")

            # 更新目标距离并处理目标丢失
            if measurements:
                strongest_detection = measurements[0]  # 已按SNR排序
                self.target_detected_range = strongest_detection['range']  # m
                self.track_loss_count = 0  # 重置丢失计数
            else:
                # 未检测到目标
                self.track_loss_count += 1
                print(f"[跟踪模式] 目标丢失 {self.track_loss_count}/{self.track_loss_threshold}")

                # 检查是否需要退回搜索模式
                if self.track_loss_count >= self.track_loss_threshold:
                    self._pending_mode_switch = RadarMode.SEARCH
                    self.target_detected_range = None
                    self.search_detection_count = 0
                    self.track_loss_count = 0
                    print(f"[雷达模式切换请求] 跟踪 -> 搜索 (目标丢失)")

            # 保存脉冲压缩矩阵副本（用于显示）
            pulse_compression_matrix_copy = self.pulse_compression_matrix.copy()

            # 重置三路积累矩阵，准备下一批脉冲
            self.pulse_compression_matrix = None
            self.pulse_compression_matrix_az_diff = None
            self.pulse_compression_matrix_el_diff = None
            self.pulse_count = 0

            result = {
                'measurements': measurements,
                'rd_map': rd_map_magnitude,  # 和通道RD图
                'rd_map_az_diff': np.abs(rd_map_az_diff) if rd_map_az_diff is not None else None,  # 方位差通道RD图
                'rd_map_el_diff': np.abs(rd_map_el_diff) if rd_map_el_diff is not None else None,  # 俯仰差通道RD图
                'range_axis': range_axis,  # m
                'doppler_axis': doppler_axis,  # Hz
                'pulse_compression_matrix': pulse_compression_matrix_copy,  # 和通道RD图输入矩阵
                'accumulated_pulses': self.coherent_integration_num,
                'coherent_integration_complete': True,
                'processing_mode': 'track'
            }

        else:
            # 未完成相参积累 - 返回当前状态
            # 计算距离轴（用于显示）
            range_gate_start = self.current_tx_data.range_gate_start  # m
            sampling_rate = self.current_tx_data.sampling_rate  # Hz
            range_gate_width = (range_bins / sampling_rate) * (self.c / 2)  # m
            range_gate_end = range_gate_start + range_gate_width
            range_axis = np.linspace(range_gate_start, range_gate_end, range_bins)  # m

            result = {
                'measurements': [],
                'range_axis': range_axis,  # m
                'accumulated_pulses': self.pulse_count,
                'coherent_integration_complete': False,
                'processing_mode': 'track_accumulating'
            }

        return result
        
    def pulse_compression(self, echo_signal: np.ndarray,
                         tx_signal: np.ndarray) -> np.ndarray:
        """脉冲压缩处理（匹配滤波）- 频域方法"""
        # 计算合适的FFT长度（避免循环卷积）
        n_fft = np.size(tx_signal) + np.size(echo_signal) - 1

        # 进行FFT（使用正确的长度）
        echo_fft = np.fft.fft(echo_signal, n_fft)
        tx_fft = np.fft.fft(tx_signal, n_fft)

        # 频域匹配滤波
        matched_filter_fft = np.conj(tx_fft)
        pc_res_freq = echo_fft * matched_filter_fft

        # 逆FFT回到时域
        compressed_signal = np.fft.ifft(pc_res_freq)

        compressed_signal = np.resize(compressed_signal, np.size(echo_signal) )

        return compressed_signal

    def _apply_receiver_gain(self, signal: np.ndarray) -> np.ndarray:
        """对回波信号应用接收机增益放大

        接收机增益放大回波信号，补偿传播损耗和提高信噪比。
        增益以dB为单位，电压放大倍数 = 10^(Gain_dB/20)

        Args:
            signal: 输入的回波信号（复数IQ信号）

        Returns:
            放大后的信号
        """
        # 获取接收机增益（dB）
        receiver_gain_db = self.params.get('receiver_gain_db', 10.0)  # 默认10dB

        # 将dB转换为线性电压增益
        # 电压增益 = 10^(dB/20)，功率增益 = 10^(dB/10)
        voltage_gain = 10**(receiver_gain_db / 20)

        # 应用增益（对复信号的每个采样点进行放大）
        amplified_signal = signal * voltage_gain

        return amplified_signal

    def _add_receiver_noise(self, signal: np.ndarray) -> np.ndarray:
        """添加接收机热噪声到IQ信号

        热噪声功率：N = k·T·B·F
        - k: 玻尔兹曼常数 = 1.38×10⁻²³ J/K
        - T: 噪声温度（通常290K）
        - B: 接收带宽（Hz）
        - F: 噪声系数（线性倍数）

        噪声电压：V = √(P·R)
        - P: 噪声功率（W）
        - R: 系统阻抗（通常50Ω）
        """
        # 物理常数
        k_boltzmann = 1.38e-23  # 玻尔兹曼常数 (J/K)
        T_noise = 290.0  # 噪声温度 (K)，室温
        Z0 = 50.0  # 系统阻抗 (Ω)

        # 获取接收带宽（与发射带宽相同）
        bandwidth = self.params.get('bandwidth', 10.0) * 1e6  # Hz

        # 获取噪声系数（dB）并转换为线性倍数
        # receiver_noise_db 现在是噪声系数（Noise Figure），单位dB
        noise_figure_db = self.params.get('receiver_noise_db', 8.0)
        noise_figure = 10**(noise_figure_db / 10)  # 线性倍数

        # 计算热噪声功率（瓦特）
        thermal_noise_power = k_boltzmann * T_noise * bandwidth * noise_figure  # W

        # 通过阻抗将功率转换为电压标准差
        # 对于复信号（I和Q），每路占总功率的一半
        # 每路的电压标准差：σ = √(P·Z0/2)
        noise_std = np.sqrt(thermal_noise_power * Z0 / 2)

        # 生成复高斯白噪声
        noise_i = np.random.normal(0, noise_std, len(signal))
        noise_q = np.random.normal(0, noise_std, len(signal))
        complex_noise = noise_i + 1j * noise_q

        # 返回叠加噪声后的信号
        return signal + complex_noise

    def extract_measurements(self, rd_map: np.ndarray, range_axis: np.ndarray,
                           doppler_axis: np.ndarray, threshold: float = None,
                           use_cfar: bool = True) -> list:
        """
        从RD图提取测量结果 - 使用CFAR检测

        Args:
            rd_map: 距离-多普勒图 (幅度)
            range_axis: 距离轴 (km)
            doppler_axis: 多普勒频率轴 (Hz)
            threshold: 固定阈值 (如果不使用CFAR)
            use_cfar: 是否使用CFAR检测

        Returns:
            检测结果列表
        """
        measurements = []

        if use_cfar:
            # CA-CFAR (Cell Averaging CFAR) 检测
            guard_cells = 2  # 保护单元
            training_cells = 8  # 训练单元
            pfa = 1e-6  # 虚警概率

            # 计算CFAR阈值因子
            N = training_cells * 2  # 总训练单元数
            alpha = N * (pfa**(-1/N) - 1)  # CFAR门限因子

            # 对RD图的每个单元进行CFAR检测
            # rd_map shape: (doppler_bins, range_bins) - FFT后的自然输出
            doppler_bins, range_bins = rd_map.shape

            for d_idx in range(guard_cells + training_cells, doppler_bins - guard_cells - training_cells):
                for r_idx in range(guard_cells + training_cells, range_bins - guard_cells - training_cells):
                    # 当前测试单元
                    # rd_map[d_idx, r_idx]: 第0维=doppler, 第1维=range
                    cut = rd_map[d_idx, r_idx]

                    # 提取训练单元（排除保护单元和测试单元）
                    # 使用滑动窗口提取周围的训练单元

                    # 定义窗口边界
                    d_start = d_idx - guard_cells - training_cells
                    d_end = d_idx + guard_cells + training_cells + 1
                    r_start = r_idx - guard_cells - training_cells
                    r_end = r_idx + guard_cells + training_cells + 1

                    # 提取完整窗口
                    window = rd_map[d_start:d_end, r_start:r_end]

                    # 创建掩码：排除保护单元和测试单元
                    window_d = window.shape[0]
                    window_r = window.shape[1]
                    mask = np.ones((window_d, window_r), dtype=bool)

                    # 排除保护单元和测试单元（中心区域）
                    center_d = training_cells
                    center_r = training_cells
                    mask[center_d - guard_cells:center_d + guard_cells + 1,
                         center_r - guard_cells:center_r + guard_cells + 1] = False

                    # 提取训练单元
                    training_cells_data = window[mask]

                    # 计算训练单元的平均噪声功率
                    if len(training_cells_data) > 0:
                        noise_power = np.mean(training_cells_data)
                    else:
                        noise_power = 0

                    # CFAR门限
                    cfar_threshold = alpha * noise_power

                    # 检测判决
                    if cut > cfar_threshold:
                        # 计算SNR
                        snr_db = 20 * np.log10(cut / noise_power) if noise_power > 0 else 0

                        # 计算速度（从多普勒频率）
                        wavelength = self.c / (self.params.get('frequency', 10.0) * 1e9)
                        velocity = -doppler_axis[d_idx] * wavelength / 2  # m/s

                        measurement = {
                            'range': range_axis[r_idx],
                            'doppler': doppler_axis[d_idx],
                            'velocity': velocity,
                            'amplitude': cut,
                            'snr': snr_db
                        }
                        measurements.append(measurement)
        else:
            # 使用固定阈值检测（回退方法）
            if threshold is None:
                threshold = np.max(rd_map) * 0.3  # 30%峰值阈值

            detections = rd_map > threshold
            detection_indices = np.where(detections)

            # rd_map shape: (doppler_bins, range_bins)
            # detection_indices[0]: doppler indices
            # detection_indices[1]: range indices
            for i in range(len(detection_indices[0])):
                doppler_idx = detection_indices[0][i]
                range_idx = detection_indices[1][i]

                # 计算速度
                wavelength = self.c / (self.params.get('frequency', 10.0) * 1e9)
                velocity = -doppler_axis[doppler_idx] * wavelength / 2  # m/s

                measurement = {
                    'range': range_axis[range_idx],
                    'doppler': doppler_axis[doppler_idx],
                    'velocity': velocity,
                    'amplitude': rd_map[doppler_idx, range_idx],
                    'snr': 20 * np.log10(rd_map[doppler_idx, range_idx] / np.mean(rd_map))
                }
                measurements.append(measurement)

        # 按SNR排序
        measurements.sort(key=lambda x: x['snr'], reverse=True)

        return measurements[:20]  # 返回前20个最强的检测


# 保持向后兼容性：RadarSignalProcessor 是 RadarMoni 的别名
RadarSignalProcessor = RadarMoni